package com.ody.util.code;

import com.odianyun.util.ResourceUtils;
import com.odianyun.util.config.ConfigContext;
import com.odianyun.util.config.PropertiesConfigContext;
import com.odianyun.util.io.FileUtils;
import com.ody.util.code.generator.ConsoleCodeGenerator;
import com.ody.util.code.generator.ExcelFileCodeGenerator;
import com.ody.util.code.generator.FileCodeGenerator;
import com.ody.util.code.generator.VueFileCodeGenerator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;

import java.util.Properties;

public class GeneratorBeanFactory implements ApplicationContextAware {
	private Properties properties;
	private ConfigContext cfg;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		Assert.notNull(properties, "properties cannot be null");
		
		PropertiesConfigContext propCfg = new PropertiesConfigContext();
		propCfg.setProperties(properties);
		propCfg.init();
		cfg = propCfg;
		
		String codeGenerators = properties.getProperty("CodeGenerator");
		Assert.notNull(properties, "CodeGenerator配置后缺失");
		
		String baseDir = properties.getProperty("baseDir");
		if (baseDir == null) baseDir = "";
		
		String[] terms = codeGenerators.split("[,]");
		for (String term : terms) {
			createBean(term.trim(), baseDir, applicationContext);
		}
	}
	
	private void createBean(String name, String baseDir, ApplicationContext applicationContext) {
		String codeTemplate = properties.getProperty(name + ".codeTemplate");
		Assert.notNull(properties, name + ".codeTemplate is not found");
		String type = properties.getProperty(name + ".type");
		String requireModel = properties.getProperty(name + ".requireModel");
		String ignoreModels = properties.getProperty(name + ".ignoreModels");
		
		BeanDefinitionBuilder builder = createBeanDefinitionBuilderByType(type, name, baseDir);
		builder.addPropertyValue("name", properties.getProperty(name + ".name", name));
		builder.addPropertyReference("modelProvider", "modelProvider");
		builder.addPropertyValue("codeTemplate", codeTemplate);
        builder.addPropertyValue("once", cfg.getBoolean(name+".once"));
        builder.addPropertyValue("skipOnEmpty", cfg.getBoolean(name+".skipOnEmpty"));
        builder.addPropertyValue("globalOnce", cfg.getBoolean(name+".globalOnce"));
        builder.addPropertyValue("skip", cfg.getBoolean(name+".skip"));
        builder.addPropertyValue("hidden", cfg.getBoolean(name+".hidden"));
		
		if (ignoreModels != null) {
			String[] ignoreModelArray = null;
			if (ignoreModels != null) {
				String[] ignoreModelItems = ignoreModels.split(",");
				ignoreModelArray = new String[ignoreModelItems.length];
				int idx = 0;
				for (String ignoreModel : ignoreModelItems) {
					ignoreModelArray[idx ++] = ignoreModel.trim();
				}
			}
			builder.addPropertyValue("ignoreModels", ignoreModelArray);
		}
		
		if (requireModel != null) {
			builder.addPropertyValue("requireModel", requireModel);
		}
		
		String beanId = name + "CodeGenerator";
		DefaultListableBeanFactory factory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
		factory.registerBeanDefinition(beanId, builder.getBeanDefinition());
	}
	
	private BeanDefinitionBuilder createBeanDefinitionBuilderByType(String type, String name, String baseDir) {
		if ("file".equals(type)) {
			return createFileBeanDefinitionBuilder(name, baseDir);
		} else if ("vueFile".equals(type)) {
            BeanDefinitionBuilder builder = createFileBeanDefinitionBuilder(name, baseDir);
            builder.getBeanDefinition().setBeanClass(VueFileCodeGenerator.class);
            builder.addPropertyValue("url", cfg.getString(name+".url"));
            builder.addPropertyValue("unzip", cfg.getBoolean(name+".unzip"));
            return builder;
        } else if ("excelFile".equals(type)) {
            BeanDefinitionBuilder builder = createFileBeanDefinitionBuilder(name, baseDir);
            builder.getBeanDefinition().setBeanClass(ExcelFileCodeGenerator.class);
            return builder;
        }
		return createConsoleBeanDefinitionBuilder(name, baseDir);
	}
	
	private BeanDefinitionBuilder createFileBeanDefinitionBuilder(String name, String baseDir) {
		String targetDir = cfg.getString(name + ".targetDir");
		Assert.notNull(targetDir, name + ".targetDir is not found");
		String filename = cfg.getString(name + ".filename");
		Assert.notNull(filename, name + ".filename is not found");
		
		baseDir = cfg.getString(name + ".baseDir", baseDir);
		
        boolean overridable = cfg.getBoolean("overridable", false);
        overridable = cfg.getBoolean(name+".overridable", overridable);
		
		boolean override = cfg.getBoolean("override", false);
		override = cfg.getBoolean(name+".override", override);
		
		boolean append = cfg.getBoolean("append", false);
		append = cfg.getBoolean(name+".append", append);
        
        if (targetDir.startsWith("classpath:") || targetDir.startsWith("file:")) {
            targetDir = ResourceUtils.getResourcePath(targetDir);
        } else {
            targetDir = FileUtils.appendSeparatorSuffix(ResourceUtils.getResourcePath(baseDir)) + targetDir;
        }
        
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FileCodeGenerator.class);
		builder.addPropertyValue("targetDir", targetDir);
		builder.addPropertyValue("filename", filename);
        builder.addPropertyValue("overridable", overridable);
		builder.addPropertyValue("override", override);
		builder.addPropertyValue("append", append);
		return builder;
	}

	private BeanDefinitionBuilder createConsoleBeanDefinitionBuilder(String name, String baseDir) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsoleCodeGenerator.class);
		return builder;
	}
	
	public Properties getProperties() {
		return properties;
	}
	public void setProperties(Properties properties) {
		this.properties = properties;
	}

}
